﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Activities;
using System.Diagnostics;
using System.Reflection;
using System.Diagnostics;

namespace BBECServiceBusAdapter.Activities
{
    /// <summary>
    /// This requires the context user have...
    ///   ...read access to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog
    ///   ...read access to HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\EventLog\Security (does not inherit from the above key)
    ///   ...write access to whatever log/source combination is used (in this case it will likely be Blackbaud\BBECServiceBusAdapter)
    /// Be sure to include 'System.Diagnostics' in the Imports section of your workflow so you can properly set the EntryType InArgument
    /// </summary>
    public sealed class WriteEventLog : CodeActivity
    {
        #region Activity Arguments
        public InArgument<string> MessageText { get; set; }
        public InArgument<Exception> Exception { get; set; }
        public InArgument<EventLogEntryType> EntryType { get; set; }
        #endregion

        protected override void Execute(CodeActivityContext context)
        {
            Log(MessageText.Get(context), Exception.Get(context), EntryType.Get(context));
        }

        public void Log(string messageText, Exception ex, EventLogEntryType eventType)
        {
            try
            {
                if (Helper.Config.LoggingLevel() == Helper.Config.LoggingLevelOption.None) { return; }

                string sourceName = Assembly.GetCallingAssembly().GetName().Name;
                string logName = "Blackbaud";

                string cr = Environment.NewLine;
                EventLogEntryType eventLogEntryType = EventLogEntryType.Information;
                string message = string.Empty;
                bool writeEntry = false;

                // Create the event source if it doesn't already exist
                if (!EventLog.SourceExists(sourceName))
                {
                    EventLog.CreateEventSource(sourceName, logName);
                }

                // add the messageText value to the message if it was provided
                if (!String.IsNullOrEmpty(messageText))
                {
                    message = "Message: " + messageText + cr + cr;
                }

                // add exception details to the message if an exception was provided
                if (ex != null)
                {
                    message += "Exception:" + cr + "  - Message: " + ex.Message + cr + "  - Stack: " + ex.StackTrace;
                    if (ex.InnerException != null)
                    {
                        message += cr + "  - Inner exception message: " + ex.InnerException.Message + cr + "  - Inner exception stack: " + ex.InnerException.StackTrace;
                    }

                    // if no eventType was provided, set the event type to Error since we received an exception, otherwise set it to the default event type
                    if (eventType == 0) // not supplied
                    {
                        eventLogEntryType = EventLogEntryType.Error;
                    }
                    else
                    {
                        eventLogEntryType = eventType;
                    }
                }
                else
                {
                    // no exception was provided, so set the eventType to what was provided or otherwise leave it at the default
                    if (eventType != 0) // value was supplied
                    {
                        eventLogEntryType = eventType;
                    }
                }

                switch (Helper.Config.LoggingLevel())
                {
                    case Helper.Config.LoggingLevelOption.Error:
                        {
                            writeEntry = (eventLogEntryType == EventLogEntryType.Error);
                            break;
                        }
                    case Helper.Config.LoggingLevelOption.Warning:
                        {
                            writeEntry = ((eventLogEntryType == EventLogEntryType.Warning) || (eventLogEntryType == EventLogEntryType.Error));
                            break;
                        }
                    default:
                        {
                            writeEntry = true;
                            break;
                        }
                }

                // write the event log entry
                if (writeEntry) { EventLog.WriteEntry(sourceName, message, eventLogEntryType); }
            }
            catch (Exception loggingException)
            {
                try
                {
                    string sourceName = Assembly.GetCallingAssembly().GetName().Name;
                    string logName = "Blackbaud";

                    // Create the event source if it doesn't already exist
                    if (!EventLog.SourceExists(sourceName))
                    {
                        EventLog.CreateEventSource(sourceName, logName);
                    }

                    EventLog.WriteEntry(sourceName, "An error occurred, and then another error occurred attempting to log:  " + loggingException.Message, EventLogEntryType.Error);
                }
                catch 
                { 
                    // oh well... 
                }
            }
        }
    }
}
